<?php
/**
 * ブログモジュール
 */
class Blog {
	private $Configs;
	private $Tags;
	private $Tags4select;
	private $Articles;

	private $Comment;
	
	public $IDList;
	public $Limit;
	public $Offset;

	public function __construct () {
		$this->Configs = null;
		$this->Tags = null;
		$this->Tags4select = null;
		$this->Modify = array();

		$this->IDList = array();
		$this->Limit = null;
		$this->Offset = null;
		
		$this->Comment = null;
	}

	public function init () {
		Y('this.getTags+', YuTori::Create($this, 'getTags'));
		Y('this.getTags4select+', YuTori::Create($this, 'getTags4select'));
		Y('this.getArticlebyID+', YuTori::Create($this, 'getArticlebyID'));
		Y('this.getArticlebyTag+', YuTori::Create($this, 'getArticlebyTag'));

		Y('this.getThumbnail+', YuTori::Create($this, 'getThumbnail'));

		Y('this.getLatestArticles+', YuTori::Create($this, 'getLatestArticles'));
		Y('this.getLatestArticleList+', YuTori::Create($this, 'getLatestArticleList'));

		Y('this.getMonthlyCount+', YuTori::Create($this, 'getMonthlyCount'));
		Y('this.getCalendar+', YuTori::Create($this, 'getCalendar'));

		Y('this.getConfigs+', YuTori::Create($this, 'getConfigs'));
		Y('this.getInformation+', YuTori::Create($this, 'getInformation'));

		// 記事リストオブジェクトの実装
		include_once (dirname(__FILE__).'/action/articlelist.class.php');
		$this->Articles = new Blog_Action_ArticleList ();

		// コメント機能の実装
		include_once (dirname(__FILE__).'/action/comment.class.php');
		$this->Comment = new Blog_Action_Comment ();
		$this->Comment->IsAccept = $this->getConfigs('IsAccept');
		$this->Comment->CommentOrder = $this->getConfigs('CommentOrder');
		$this->Comment->CommentMax = $this->getConfigs('CommentMax');
		$this->Comment->CommentTable = 'blog_comments';

		$this->Comment->Article = &$this->Articles->Now;

		Y('this.getCommentCount+', YuTori::Create($this->Comment, 'getCommentCount'));
		Y('this.getComments+', YuTori::Create($this->Comment, 'getComments'));
		Y('this.getLatestComments+', YuTori::Create($this->Comment, 'getLatestComments'));
		
		Y('this.Admin+', YuTori::Create($this, 'callAdmin'));
		Y('this.updateTags+', YuTori::Create($this, 'updateTags'));

		Y('this.Plugins+', array());
	}

	/**
	 * 単独コールは引数で動作を変更する
	 */
	public function call ($Act = null) {
		Y('Logger.Debug', 'Blogコール Act='.$Act);
		$Act = strtolower($Act);
		switch ($Act) {
			case ('commentreceive'):
				$this->getArticlebyID(Y('Request.POST.ArticleID'), false);
				$this->Comment->receiveComment();
				break;
			case ('rss'):
				$this->rssOutput();
				break;
			default:
				break;
		}
		return true;
	}

	/**
	 * configを取得する
	 */
	public function getConfigs ($Column = null) {
		Y('Logger.Debug', 'Config取得要求 '.$Column);
		if ($this->Configs && $Column) {
			if (isset($this->Configs[$Column])) {
				return $this->Configs[$Column];
			} else {
				return false;
			}
		} elseif ($this->Configs) {
			return $this->Configs;
		}
		
		$Query = array ('query'=>'select', 'table'=>'blog_configs', 'where'=>'ID=1');
		Y('Helper.loadAction', 'ConnectDB', $Query);
		
		$this->Configs = Y('Helper.List.ConnectDB.com.Result');

		if ($Column) {
			if (isset($this->Configs[$Column])) {
				return $this->Configs[$Column];
			} else {
				return false;
			}
		} else {
			return $this->Configs;
		}
	}

	/**
	 * お知らせを取得する
	 */
	public function getInformation () {
		if (!$this->Configs) $this->getConfigs();
		return @$this->Configs['Information'];
	}

	/**
	 * 最新のn件リストを取得する
	 */
	public function getLatestArticleList ($Num = 10) {
		$Query = array (
			'query'=>'select',
			'table'=>'blog_articles',
			'column'=>'ID, Title, ViewDay',
			'where'=>'IsOpen=1 AND IsTop=1',
			'order'=>'ViewDay DESC, ID DESC',
			'limit'=>(int)$Num,
		);
		Y('Helper.loadAction', 'ConnectDB', $Query);
		
		return Y('Helper.List.ConnectDB.com.ResultAll');
	}

	/**
	 * トップ表示n件を取得する
	 */
	public function getLatestArticles ($Num = 10, $Modify = true) {
		$Query = array (
			'query'=>'select',
			'table'=>'blog_articles',
			'where'=>'IsOpen=1 AND IsTop=1',
			'order'=>'ViewDay DESC, ID DESC',
			'limit'=>(int)$Num,
		);
		Y('Helper.loadAction', 'ConnectDB', $Query);
		
		$Articles = Y('Helper.List.ConnectDB.com.ResultAll');
		foreach ($Articles as $Key=>$ArticleData) {
			// タグの取得
			$ArticleData['Tag'] = array();
			$Query = array ('query'=>'select', 'table'=>'blog_tags', 'data'=>array('ArticleID'=>(int)$ArticleData['ID']));
			Y('Helper.loadAction', 'ConnectDB', $Query);
			while ($Tag = Y('Helper.List.ConnectDB.com.Result')) {
				$ArticleData['Tag'][] = $Tag['Tag'];
			}
			
			$this->Articles->registArticle($ArticleData, $Modify);
		}
		
		// getNeighborの定義
		// TODO:未実装
//		Y('Module.List.Blog.getNeighbor+', YuTori::Create($this, 'getNeighborLatests'));

		return $this->Articles->getArticleListArray();
	}

	/**
	 * 月別の更新カウント
	 */
	public function getMonthlyCount ($DateFormat = 'Y-m') {
		$Query = array (
			'query'=>'select',
			'column'=>'ID, ViewDay',
			'table'=>'blog_articles',
			'where'=>'IsOpen=1',
			'order'=>'ViewDay DESC,ID DESC'
		);
		Y('Helper.loadAction', 'ConnectDB', $Query);
		
		$Articles = Y('Helper.List.ConnectDB.com.ResultAll');
		$Archive = array();
		foreach ($Articles as $Key=>$ArticleData) {
			$Month = date($DateFormat, $ArticleData['ViewDay']);
			if (empty($Archive[$Month])) {
				$Archive[$Month]['count'] = 1;
			} else {
				$Archive[$Month]['count']++;
			}
			$Archive[$Month]['ID'] = $ArticleData['ID'];
		}
		
		return $Archive;
	}

	/**
	 * IDから1つの記事を取得する
	 * コメント投稿はこのメソッドの使用後のみとなる。
	 */
	public function getArticlebyID ($ID, $Modify = false) {
		$ID = (int)$ID;
		// すでに取得したデータとの照合
/*		if ($this->Article && $this->Article->Article['ID'] == $ID) {
			if ($Modify) {
				if (!$this->Article->Modify) {
					$this->Article->Modify = $this->Article->Article;
					$this->Article->modifyArticle();
				}

				return $this->Article->Modify;
			} else {

				return $this->Article->Article;
			}
		}*/

		// 本体データの取得
		$Query = array ('query'=>'select', 'table'=>'blog_articles', 'data'=>array('ID'=>$ID));
		Y('Helper.loadAction', 'ConnectDB', $Query);
		if (!$ArticleData = Y('Helper.List.ConnectDB.com.Result')) return false;

		// LastViewを更新
		// セッション「Blog」が既にあってもなくても同じ動作になるようにしておく。
		Y('Helper.loadAction', 'Session');
		$BlogSession = Y('Helper.list.Session.Vars.Blog');
		$BlogSession['LastView'] = (int)$ID;
		Y('Helper.list.Session.Vars.Blog+', $BlogSession);

		// タグの取得
		$ArticleData['Tag'] = array();
		$Query = array ('query'=>'select', 'table'=>'blog_tags', 'data'=>array('ArticleID'=>(int)$ID));
		Y('Helper.loadAction', 'ConnectDB', $Query);
		$i = 1;
		while ($Tag = Y('Helper.List.ConnectDB.com.Result')) {
			$ArticleData['Tag'][] = $Tag['Tag'];
		}

		// getNeighborの定義
		Y('Module.List.Blog.getNeighbor+', YuTori::Create($this, 'getNeighborSingle'));

		$this->Articles->registArticle($ArticleData, $Modify);
		return $this->Articles->getArticle(0);
	}

	/**
	 * 単独記事表示時のprev/next
	 * 基本的に表示日付順
	 */
	public function getNeighborSingle ($Side = 'next') {
		$Side = strtolower ($Side);
		$Key = 'ViewDay';
		switch ($Side) {
			case ('next') :
				// 次の記事の取得
				$Where = '`IsOpen`=1 AND (`'.$Key.'` > '.$this->Article[$Key].' OR (`'. $Key .'`= '.$this->Article[$Key].' AND `ID` >'.$this->Article['ID'].'))';
				$Order = $Key. ', ID';
				break;
			case ('prev') :
				$Where = '`IsOpen`=1 AND (`'.$Key.'` < '.$this->Article[$Key].' OR (`'. $Key .'`= '.$this->Article[$Key].' AND `ID` <'.$this->Article['ID'].'))';
				$Order = $Key. ' DESC, ID DESC';
				break;
			default:
				return false;
		}
		Y('Helper.loadAction', 'ConnectDB', array(
			'table'=>'blog_articles',
			'query'=>'select',
			'column'=>'ID',
			'where'=>$Where,
			'order'=>$Order,
			'limit'=>1
		));
		
		if ($ID = Y('Helper.List.ConnectDB.com.Result')) return $ID['ID'];
		return false;
	}

	/**
	 * タグから記事を取得する
	 */
	public function getArticlebyTag ($Tag, $Limit, $Offset, $Modify = false) {
		// 本体データの取得
		$Query = array ('query'=>'select', 'column'=>'ArticleID', 'table'=>'blog_tags', 'data'=>array('Tag'=>$Tag));
		Y('Helper.loadAction', 'ConnectDB', $Query);
		if (!$this->IDList = Y('Helper.List.ConnectDB.com.ResultAll')) return false;
		$this->Limit = (int) $Limit;
		$this->Offset = (int) $Offset;

		foreach ($this->IDList as &$ID) {
			$ID = $ID['ArticleID'];
		}

		$IDList = implode ($this->IDList, ',');

		$Query = array ('query'=>'select', 'column'=>'*', 'table'=>'blog_articles', 'where'=>'ID IN ('.$IDList.')', 'limit'=>$this->Limit, 'offset'=>$this->Offset);
		Y('Helper.loadAction', 'ConnectDB', $Query);
		while ($ArticleData = Y('Helper.List.ConnectDB.com.Result')) {
			$this->Articles->registArticle($ArticleData, $Modify);
		}

		// getNeighborの定義
		Y('Module.List.Blog.getNeighbor+', YuTori::Create($this, 'getNeighborTag'));
		
		return $this->Articles->getArticleListArray();
	}

	/**
	 * タグ検索時のprev/next
	 */
	public function getNeighborTag ($Side = 'next') {
		$Side = strtolower ($Side);
		switch ($Side) {
			case ('next') :
				// 次の記事の取得
				$Now = $this->Offset + $this->Limit;
				$IDNum = count ($this->IDList);
				if ($IDNum > $Now) return $Now;
				break;
			case ('prev') :
				if ($this->Offset > 0) {
					if (($Prev = $this->Offset - $this->Limit) < 0) $Prev = 0;
					return $Prev;
				}
				break;
			default:
				break;
		}
		
		return false;
	}


	/**
	 * タグの一覧を取得する（配列形式）
	 */
	public function getTags () {
		if ($this->Tags) return $this->Tags;
		$Tags = array();
		$Query = array ('query'=>'select', 'table'=>'blog_tags', 'group'=>'Tag', 'order'=>'count(*)', 'column'=>'Tag');
		Y('Helper.loadAction', 'ConnectDB', $Query);
		while ($Tag = Y('Helper.List.ConnectDB.com.Result')) {
			$Tags[] = $Tag['Tag'];
		}
		
		$this->Tags = $Tags;
		return $Tags;
	}

	/**
	 * 月間の更新カレンダーを取得する
	 */
	public function getCalendar ($Month, $Year = 0) {
		$Month = (int) $Month;
		if ($Month < 1 || $Month > 12) return false;
		if (!$Year) $Year = (int)date('Y');
		
		$StartTime = mktime(0,0,0,$Month,1, $Year);
		$EndTime = mktime(23,59,59,$Month+1,0, $Year);
		
		$Query = array (
			'query'=>'select',
			'column'=>'ID, ViewDay',
			'table'=>'blog_articles',
			'where'=>'IsOpen=1 AND ViewDay >= '.$StartTime. ' AND ViewDay <= '.$EndTime,
			'order'=>'ViewDay,ID DESC'
		);
		Y('Helper.loadAction', 'ConnectDB', $Query);
		
		$Articles = Y('Helper.List.ConnectDB.com.ResultAll');
		$Archive = array();
		$Day = 1;
		while (checkdate($Month, $Day, $Year)) {
			$Archive[$Day] = false;
			$Day++;
		}

		foreach ($Articles as $ArticleData) {
			$Day = (int)date('d', $ArticleData['ViewDay']);
			$Archive[$Day] = $ArticleData;
		}

		return $Archive;
	}

	/**
	 * サムネイル(thumbnail.jpg)をアップロードしてあるかどうか判定し、ある場合アドレスを返す。
	 */
	function getThumbnail ($ArticleID) {
		$ArticleID = (int) $ArticleID;
		Y('Module.load', 'Upload');
		if ($Res = Y('Module.list.Upload.isFileExist', 'thumbnail.jpg', 'attach', 'blog', $ArticleID)) {
			return 'blog/attach/'.$ArticleID.'/original/thumbnail.jpg';
		} else {
			return false;
		}
		
	}

/***************************** Adminモード用 ****************************/
	/**
	 * タグの一覧を取得する（オプションリスト形式）
	 */
	public function getTags4select () {
		if ($this->Tags4select) return $this->Tags4select;
		$Tags = array();
		$Query = array ('query'=>'select', 'table'=>'blog_tags', 'group'=>'Tag', 'order'=>'count(*)', 'column'=>'Tag');
		Y('Helper.loadAction', 'ConnectDB', $Query);
		while ($Tag = Y('Helper.List.ConnectDB.com.Result')) {
			$Tags[$Tag['Tag']] = $Tag['Tag'];
		}
		
		$this->Tags4select = $Tags;
		return $Tags;
	}

	/**
	 * タグをアップデートする(Editor用)
	 */
	public function updateTags () {
		if (!Y('Module.list.Editor$')) return false;
		// アップデートかインサートか判定
		$ID = Y('Module.list.Editor.Data');
		$Tags = isset ($ID['Tag'])?($ID['Tag']):(false);
		$ID = isset ($ID['ID'])?($ID['ID']):(false);
		if (!$ID) {
			if (empty(Y('Module.list.Editor$')->Xml['newID'])) return false;
			$ID = Y('Module.list.Editor$')->Xml['newID'];
		}

		Y('Logger.Debug', 'タグ挿入開始 '. $ID);
		var_dump ($ID);

		$Query = array ('query'=>'delete', 'table'=>'blog_tags', 'data'=>array('ArticleID'=>$ID));
		Y('Helper.loadAction', 'ConnectDB', $Query);
		foreach ((array)$Tags as $Tag) {
			if (!$Tag) continue;
			$Query = array ('query'=>'insert', 'table'=>'blog_tags', 'data'=>array('ArticleID'=>$ID, 'Tag'=>$Tag));
			Y('Helper.List.ConnectDB', $Query);
		}
		return true;
	}

	/**
	 * admin動作
	 */
	public function callAdmin () {
		$Act = Y('Request.Global.Act');
		Y('Request.Global');
		switch ($Act) {
			case 'article':
				Y('Module.loadAction', 'Editor', 'Blog', 'article.yml', Y('Request.Global.Act'), Y('Request.Global.Op'), Y('Document.toObject$'));
				break;
			case 'comment':
				Y('Module.loadAction', 'Editor', 'Blog', 'comment.yml', Y('Request.Global.Act'), Y('Request.Global.Op'), Y('Document.toObject$'));
				break;
			case 'accept':
				$this->admin_AcceptComment();
				break;
			case 'config':
				Y('Module.loadAction', 'Editor', 'Blog', 'config.yml', Y('Request.Global.Act'), Y('Request.Global.Op'), Y('Document.toObject$'));
				break;
			default:
				break;
		}
		return true;
	}

	/**
	 * 未承認コメント一覧
	 */
	private function admin_AcceptComment () {
		$Cnt = 0;

		// POSTがあるかないか
		if ($Acp = Y('Request.Post.acp')) {
			foreach ((array)$Acp as $Key=>$Val) {
				if ($Val) {
					Y('Helper.loadAction', 'ConnectDB', array(
						'table'=>'blog_comments',
						'query'=>'update',
						'data'=>array('IsAccept'=>(int)$Val),
						'where'=>'ID='.((int)$Key),
					));
					$Cnt++;
				}
			}
		}

		Y('Output$')->AcceptCount = $Cnt;
		Y('Output.Body', 'Blog:admin/accept');
		return true;
	}

	/**
	 * RSS出力
	 */
	public function rssOutput ($Num = 10) {
		$ArticleList = $this->getLatestArticles((int)$Num, false);
		
		Y('Helper.load', 'xmlOutput');
		Y('Helper.list.XMLOutput.initRoot', 'rss');
		Y('Helper.list.XMLOutput.Root.Attr', 'version', '2.0');

		Y('Helper.list.XMLOutput.Root.addChild', 'channel');

		Y('Helper.list.XMLOutput.addChild', 'channel', 0, 'title');
		Y('Helper.list.XMLOutput.setText', 'title', 0, Y('App.Config.Site.SiteName'));

		Y('Helper.list.XMLOutput.addChild', 'channel', 0, 'link');
		Y('Helper.list.XMLOutput.setText', 'link', 0, Y('App.Config.Site.BaseURL'));

		Y('Helper.list.XMLOutput.addChild', 'channel', 0, 'description');
		Y('Helper.list.XMLOutput.setText', 'description', 0, Y('App.Module.Blog.RSS.Description'));

		Y('Helper.list.XMLOutput.addChild', 'channel', 0, 'pubDate');
		Y('Helper.list.XMLOutput.setText', 'pubDate', 0, gmstrftime('%Y-%m-%dT%H:%M:%S+09:00'));

		foreach ($ArticleList as $Key=>$Article) {

			Y('Helper.list.XMLOutput.addChild', 'channel', 0, 'item');
			Y('Helper.list.XMLOutput.addChild', 'item', $Key, 'title');
			Y('Helper.list.XMLOutput.setText', 'title', $Key+1, $Article['Title']);

			Y('Helper.list.XMLOutput.addChild', 'item', $Key, 'link');
			Y('Helper.list.XMLOutput.setText', 'link', $Key+1, Y('App.Module.Blog.RSS.BaseURL').$Article['ID']);

			Y('Helper.list.XMLOutput.addChild', 'item', $Key, 'description');
			Y('Helper.list.XMLOutput.setText', 'description', $Key+1, mb_strimwidth(strip_tags($Article['Text']), 0, 200, '...', 'UTF-8'));

			Y('Helper.list.XMLOutput.addChild', 'item', $Key, 'pubDate');
			$pubDate = $Article['ViewDay'] + ($Article['RegistTime'] % 86400);
			Y('Helper.list.XMLOutput.setText', 'pubDate', $Key+1,
				strftime('%Y-%m-%dT%H:%M:%S+09:00', $pubDate)
			);
		}
		return true;
	}
}
?>